package com.zkh.myutils.bean;

import com.zkh.myutils.annotation.Format;
import com.zkh.myutils.annotation.Ignore;
import com.zkh.myutils.io.log.Logger;
import com.zkh.myutils.utils.DateUtils;
import com.zkh.myutils.utils.StringUtils;
import com.zkh.myutils.utils.Utils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.Timestamp;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 实体类转换工具类，不依赖第三方jar包。
 * @author zkh
 */
public class BeanUtils {
	//全局转换器
	static TypeConverterManager globalConverterManager = new TypeConverterManager();
	//原始类型
	public static final Map<String, String> BASE_DESC_MAP = Utils.getMap(new HashMap<>(), "byte", "B", "short", "S", "int", "I", "long", "J", "float", "F", "double", "D", "boolean", "Z", "char", "C");
	
	/**
	 * 注册转换器，全局方法，一个地方设置，所有地方均会生效
	 * @param typeConverterManager 类型转换器管理器
	 */
	public static void setConverter(TypeConverterManager typeConverterManager) {
		globalConverterManager = typeConverterManager;
	}

	/**
	 * List元素项转Bean集合
	 * @param list 待转换的List集合
	 * @param targetTypeClass 需要转换的目标集合元素类型
	 */
	public static <T> List<T> listToBean(List<?> list, Class<T> targetTypeClass){
		return listToBean(list, targetTypeClass, null);
	}

	/**
	 * List集合转换
	 * @param list 待转换的List集合
	 * @param targetTypeClass 需要转换的目标集合元素类型
	 * @param converterManager 转换器管理器
	 * @return 转换后的集合
	 */
	public static <T> List<T> listToBean(List<?> list, Class<T> targetTypeClass, TypeConverterManager converterManager){
		return listToBean(list, List.class, null, targetTypeClass, converterManager);
	}

	/**
	 * List元素项转Bean集合
	 * @param list 待转换的List集合
	 * @param genericType 泛型类型
	 */
	public static <T> List<T> listToBean(List<?> list, String genericType){
		return listToBean(list, genericType, null);
	}

	/**
	 * List集合转换
	 * @param list 待转换的List集合
	 * @param genericType 泛型类型
	 * @param converterManager 转换器管理器
	 * @return 转换后的集合
	 */
	public static <T> List<T> listToBean(List<?> list, String genericType, TypeConverterManager converterManager){
		return listToBean(list, List.class, genericType, null, converterManager);
	}

	/**
	 * List集合转换
	 * @param list 待转换的List集合
	 * @param toTypeClass 目标类型
	 * @param genericType 目标类型泛型类型
	 * @param targetTypeClass 目标项类型Class
	 * @param converterManager 转换器管理器
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static <T> List<T> listToBean(List<?> list, Class<? extends List> toTypeClass, String genericType,
			Class<T> targetTypeClass, TypeConverterManager converterManager){
		//检查空
		Objects.requireNonNull(list);
		//转换
		return (List<T>) BeanArrayConverter.toList(list, List.class, toTypeClass, genericType, targetTypeClass, converterManager);
	}

	/**
	 * Map转实体对象
	 * @param map Map容器
	 * @param bean 待转换对象
	 */
	public static <T> T mapToBean(Map<String, ?> map, Class<T> bean){
		return mapToBean(map, bean, null);
	}

	/**
	 * Map转实体对象
	 * @param map Map容器
	 * @param bean 待转换对象
	 * @param converterManager 转换器管理器
	 * @return 转换后的对象
	 */
	public static <T> T mapToBean(Map<String, ?> map, Class<T> bean, TypeConverterManager converterManager){
		return mapToBean(map, bean, null, converterManager);
	}

	/**
	 * Map转实体对象
	 * @param map Map容器
	 * @param bean 待转换对象
	 * @param genericType 泛型类型。当bean参数为Map时，需要指定泛型类型，才能解析到具体类型
	 * @param converterManager 转换器管理器
	 * @return 转换后的对象
	 */
	@SuppressWarnings({"rawtypes", "unchecked"})
	public static <T> T mapToBean(Map<String, ?> map, Class<T> bean, String genericType, TypeConverterManager converterManager){
		//目标对象是Map
		if(Map.class.isAssignableFrom(bean)){
			//指定了泛型类型
			if(StringUtils.isNotEmpty(genericType)) {
				try {
					//创建结果容器
					Map<String, Object> ctnMap = bean.isInterface() ? new HashMap<>() : (Map) bean.newInstance();
					//解析泛型
					String[] keyValueType = parseMapGenericType(genericType);
					//遍历Map
					for(Map.Entry<String, ?> kv : map.entrySet()) {
						//获取值
						Object v = kv.getValue();
						//有值
						if(v!=null) {
							//目标类型
							Class<?> toTypeClass = Class.forName(checkDesc(keyValueType[1]));
							//转换值
							v = getConvertValue(v, toTypeClass, toTypeClass.getName(), keyValueType[2], converterManager);
						}
						//保存值
						ctnMap.put(kv.getKey(), v);
					}
					return (T) ctnMap;
				}catch (InstantiationException | IllegalAccessException e) {
					throw new TypeConverterException("实例化失败：" + bean.getName(), e);
				}catch (ClassNotFoundException e) {
					throw new RuntimeException(e);
				}
			}
			return (T) map;
		}
		//实体类
		T en;
		//实例化实体类
		try{
			en = bean.newInstance();
		} catch (IllegalAccessException e) {
			throw new TypeConverterException("请提供可访问的无参构造器", e);
		} catch (InstantiationException e) {
			throw new TypeConverterException("实体类实例化失败", e);
		}
		//待转换的Bean.class（内部会被父类依次重写，直至解析到父类为Object为止）
		Class<?> cls = bean;
		//目标对象不为空且不是Object
		while(cls!=null && !Type.OBJECT.equals(cls.getName())) {
			//获取所有字段
			Field[] fields = cls.getDeclaredFields();
			//遍历字段进行转换
			for(Field field: fields){
				//有明确忽略注解
				if(field.isAnnotationPresent(Ignore.class)){
					continue;
				}
				//字段类型
				Class<?> toTypeClass = field.getType();
				String toTypeName = toTypeClass.getName();
				//字段名称
				String fieldName = field.getName();
				//修饰符
				int modifiers = field.getModifiers();
				//常量
				if(Modifier.isFinal(modifiers)){
					Logger.debug("final字段不设置值：{}", fieldName);
				}//静态字段
				else if(Modifier.isStatic(modifiers)){
					Logger.debug("静态字段不设置值：{}", fieldName);
				}else{
					//从Map中获取匹配到的字段值
					Object value = map.get(fieldName);
					//只处理有值情况
					if(value!=null){
						//实体setter
						String setter = "set" + StringUtils.uppercaseFirst(fieldName);
						//setter方法
						try{
							//获取Setter
							Method method = cls.getMethod(setter, toTypeClass);
							//转换后的值
							Object result = getConvertValue(value, toTypeClass, toTypeName, genericType, converterManager);
							//调用Setter
							method.invoke(en, result);
						}catch (NoSuchMethodException e){
							Logger.warn("字段{}未提供Setter，无法设置数据", fieldName);
						}catch (SecurityException | IllegalAccessException | IllegalArgumentException e){
							Logger.warns(setter + "方法调用失败，自动跳过", e);
						}catch (InvocationTargetException e){
							Logger.errors(setter + "方法内部执行发生异常，赋值失败，自动跳过", e.getTargetException());
						}
					}
				}
			}
			//父类
			cls = cls.getSuperclass();
		}
		return en;
	}
	
	/**
	 * 将Map&lt;String, String[]&gt;转为实体类，只针对web项目参数request.getParameterMap()进行解析。通用Map转实体请调用mapToBean
	 * @param map 键值为字符串的Map
	 * @param bean 要转换的实体类
	 */
	@SuppressWarnings("unchecked")
	public static <T> T parameterToBean(Map<String, String[]> map, Class<T> bean){
		//实体类
		T en;
		//实例化实体类
		try {
			//目标对象是Map，则直接返回
			if(Map.class.getName().equals(bean.getName())) {
				return (T) map;
			}
			en = bean.newInstance();
		} catch (IllegalAccessException e) {
			throw new TypeConverterException("请提供可访问的无参构造器");
		} catch (InstantiationException e) {
			throw new TypeConverterException("实体类实例化失败");
		}
		//map为空
		if(map==null || map.isEmpty()) {
			return en;
		}
		//Class
		Class<?> cls = bean;
		//目标对象不为空且不是Object
		while(cls!=null && !Type.OBJECT.equals(cls.getName())) {
			//获取字段
			Field[] fields = cls.getDeclaredFields();
			//遍历
			for(Field f: fields) {
				//忽略
				if(f.isAnnotationPresent(Ignore.class)) {
					continue;
				}
				//字段名
				String fieldName = f.getName();
				//有该字段值
				if(map.containsKey(fieldName)) {
					//setter
					String setter = "set" + StringUtils.uppercaseFirst(f.getName());
					//赋值
					try {
						//类型Class
						Class<?> toTypeClass = f.getType();
						//字段类型
						String toTypeName = toTypeClass.getName();
						//Map中的值
						String[] val = map.get(fieldName);
						//获取对应setter
						Method m = bean.getMethod(setter, toTypeClass);
						//值不为空
						if(val!=null) {
							//单值
							String singleValue = val.length==0 ? null : val[0];
							//字符串
							if(Type.STRING.equals(toTypeName)) {
								m.invoke(en, singleValue);
							}
							//日期类型
							if(StringUtils.isExist(toTypeName, Type.DATE, Type.TIMESTAMP)) {
								//默认解析格式
								String pattern = "yyyy-MM-dd HH:mm:ss";
								//获取是否有注解
								Format fmt = f.getAnnotation(Format.class);
								//有注解
								if(fmt!=null) {
									pattern = fmt.pattern();
								}
								//值
								Date date = DateUtils.getDate(singleValue, pattern);
								//保存值
								m.invoke(en, Type.DATE.equals(toTypeName) ? date : new Timestamp(date.getTime()));
							}//整体类型
							else if(WholeTypeConverter.TYPE_WHOLE_LIST.contains(toTypeName)) {
								m.invoke(en, WholeTypeConverter.cast(singleValue, Type.STRING, toTypeName));
							}
							//数组
							else if(toTypeClass.isArray()) {
								//数组转换
								m.invoke(en, BeanArrayConverter.toArray(val, val.getClass(), toTypeClass));
							}
							//集合
							else if(BeanArrayConverter.collectionType.contains(toTypeName)) {
								m.invoke(en, BeanArrayConverter.toList(val, val.getClass(), toTypeClass, f.getGenericType().getTypeName()));
							}
						}
					}catch (NoSuchMethodException e){
						Logger.warn("字段{}未提供Setter，无法设置数据", fieldName);
					}catch (SecurityException | IllegalAccessException | IllegalArgumentException e){
						Logger.warns(setter + "方法调用失败，自动跳过", e);
					}catch (InvocationTargetException e){
						Logger.errors(setter + "方法内部执行发生异常，赋值失败，自动跳过", e.getTargetException());
					}
				}
			}
			//父类
			cls = cls.getSuperclass();
		}
		return en;
	}
	
	/**
	 * 检查描述符
	 * @param typeDesc 类型描述符
	 * @return 返回可以Class.forName的字符串
	 */
	private static String checkDesc(String typeDesc) {
		//数组符号
		int idx = typeDesc.indexOf("[]");
		//不是数组
		if(idx==-1) {
			return typeDesc;
		}
		//一维数组
		if(idx==typeDesc.length()-2) {
			//截取类型
			typeDesc = typeDesc.substring(0, typeDesc.length()-2);
			//获取原始类型
			String type = BASE_DESC_MAP.get(typeDesc);
			//如果是原始类型
			if(type!=null) {
				return "[" + type;
			}
			return "[L" + typeDesc + ";";
		}
		throw new TypeConverterException("不支持多维数组的转换：" + typeDesc);
	}
	
	/**
	 * 获取转换值
	 * @param value 转换前
	 * @param toTypeClass 目标类型
	 * @param toTypeName 目标类型名称
	 * @param genericType 泛型类型
	 * @param converterManager 转换器管理器
	 * @return 转换后的值
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static Object getConvertValue(Object value, Class<?> toTypeClass, String toTypeName, String genericType, TypeConverterManager converterManager) {
		//转换后的值
		Object result;
		//获取转换器
		TypeConverter converter = converterManager!=null ? converterManager.getConverter(toTypeClass) : null;
		//全局转换器
		if(converter==null){
			converter = BeanUtils.globalConverterManager.getConverter(toTypeClass);
		}
		//结果值类型
		Class<?> fromTypeClass = value.getClass();
		String fromTypeName = fromTypeClass.getName();
		//自定义转换器
		if(converter!=null){
			result = converter.convertToProperty(value);
		}//整体类型
		else if(WholeTypeConverter.TYPE_WHOLE_LIST.contains(toTypeName)){
			result = WholeTypeConverter.cast(value, fromTypeName, toTypeName);
		}//数组
		else if(toTypeClass.isArray()){
			//获取并保存值
			result = BeanArrayConverter.toArray(value, fromTypeClass, fromTypeName, toTypeClass, toTypeName);
		}//集合
		else if(BeanArrayConverter.collectionType.contains(toTypeName)){
			//获取并保存值
			result = BeanArrayConverter.toList(value, fromTypeClass, fromTypeName, toTypeClass, toTypeName, genericType);
		}
		//其余视为Map转换Object
		else{
			//value值转Map
			if(value instanceof Map){
				result = mapToBean((Map<String, ?>) value, toTypeClass, genericType, converterManager);
			}else{
				throw new TypeConverterException("类型"+fromTypeName+"无法转换为类型"+toTypeName);
			}
		}
		//返回结果
		return result;
	}
	
	/**
	 * 解析Map泛型类型
	 * @param genericType 泛型类型
	 * @return 泛型解析结果，[key, value, 下级泛型]
	 */
	private static String[] parseMapGenericType(String genericType) {
		//获取泛型类型
		genericType = genericType.substring(genericType.indexOf('<')+1, genericType.length()-1);
		//分隔符
		int sep = genericType.indexOf(',');
		//key
		String key = genericType.substring(0, sep);
		//不是字符串
		if(!Type.STRING.equals(key)) {
			throw new TypeConverterException("Map的键仅支持String类型");
		}
		//value
		String value = genericType.substring(sep+1).trim();
		//内部还有泛型
		sep = value.indexOf('<');
		//有泛型
		if(sep>-1) {
			return new String[] {key, value.substring(0, sep), value};
		}
		return new String[] {key, value, null};
	}
}
